﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using InterfacesTransformation;
using DTO;
using Utilities;
using Utilities.ArrayLib;
using Utilities.StatisticLib;

namespace MyDuplicationRemover
{
    public partial class StructureDuplicationRemoverPlugin : UserControl, IDuplicationRemover
    {
        public StructureDuplicationRemoverPlugin()
        {
            InitializeComponent();
        }

        private IHost pluginHost = null;
        public IHost PluginHost
        {
            get
            {
                return pluginHost;
            }
            set
            {
                this.pluginHost = value;
            }
        }

        private string pluginName = "Duplication Remover By Structure";
        public string PluginName
        {
            get
            {
                return this.pluginName;
            }
        }

        private string pluginDescription = "Remove duplication of webpages in a group by their structures.";

        public string PluginDescription
        {
            get { return pluginDescription; }
            set { pluginDescription = value; }
        }

        public UserControl PluginUserControl
        {
            get
            {
                return this;
            }
        }

        public void Initialize()
        {
        }

        public void PluginDispose()
        {
        }

        #region Interface functions implementation
        public void RemoveDuplication(List<Webpage> curGroup)
        {
            RemoveDuplicationGroupWithEvaluation(
                curGroup,
                Convert.ToInt32(numericUpDownPercentageOfSeeds.Value),
                Convert.ToInt32(numericUpDownMinNumOfSeeds.Value),
                Convert.ToInt32(numericUpDownPercentageOfSamples.Value),
                Convert.ToInt32(numericUpDownMinNumOfSamples.Value)
                );
        }

        public int MatchTwoWebtags(Webtag wtag1, Webtag wtag2)
        {
            // If their name or their number of children is different
            if ((wtag1.Name != wtag2.Name) || (wtag1.Children.Count != wtag2.Children.Count))
            {
                wtag1.IsDup = wtag2.IsDup = -1;
                return -1;
            }
            else
            {
                if ((wtag1.Children.Count == 0) && (wtag2.Children.Count == 0))
                {
                    if (wtag1.Text.CompareTo(wtag2.Text) == 0)
                    {
                        wtag1.IsDup = wtag2.IsDup = 1;
                        return 1;
                    }
                    else
                    {
                        wtag1.IsDup = wtag2.IsDup = 0;
                        return 0;
                    }
                }

                bool isSameStructure = true; // check if they have the same structure
                for (int i = 0; i < wtag1.Children.Count; i++) // all their children have the same name correlatively
                {
                    if (wtag1.Children[i].Name != wtag2.Children[i].Name)
                    {
                        isSameStructure = false;
                        break;
                    }
                }

                if (isSameStructure == true)
                {
                    wtag1.IsDup = wtag2.IsDup = 0; // they're marked to have the same structure first

                    bool isDuplicated = true; // check if two tags are duplicated
                    for (int i = 0; i < wtag1.Children.Count; i++) // if all their children are duplicated correlatively
                    {
                        if (MatchTwoWebtags(wtag1.Children[i], wtag2.Children[i]) != 1)
                        {
                            isDuplicated = false;
                        }
                    }

                    if (isDuplicated == true)
                    {
                        wtag1.IsDup = wtag2.IsDup = 1;
                        return 1;
                    }
                    else
                    {
                        return 0;
                    }
                }
                else // they are different
                {
                    wtag1.IsDup = wtag2.IsDup = -1;
                    return -1;
                }
            }
        }

        public int MeasureSimilarity(Webtag wtag)
        {
            int sim = 0;
            switch (wtag.IsDup)
            {
                case -1:
                    return 0;
                case 0:
                    foreach (Webtag wt in wtag.Children)
                    {
                        sim += MeasureSimilarity(wt);
                    }
                    break;
                case 1:
                    if (wtag.Parent == null || wtag.Parent.IsDup == 0)
                    {
                        int height = MeasureHeight(wtag);
                        int weight = 1;
                        foreach (Webtag wt in wtag.Children)
                        {
                            weight += MeasureSimilarity(wt);
                        }
                        sim = height * weight;
                    }
                    else
                    {
                        sim = 1;
                        foreach (Webtag wt in wtag.Children)
                        {
                            sim += MeasureSimilarity(wt);
                        }
                    }
                    break;
            }
            return sim;
        } 
        #endregion

        #region Fix Functions for this plugin

        /// <summary>
        /// Remove duplication in a group of webpages with parameters.
        /// </summary>
        /// <param name="curGroup">List of webpages in a group</param>
        /// <param name="percentOfSeed">The percentage of webpage to be chosen as seeds</param>
        /// <param name="minNumOfSeed">The minimum number of seeds chosen from this group</param>
        /// <param name="percentOfSample">The percentage of webpage to be chosen as samples exclude seeds</param>
        /// <param name="minNumOfSample">The minimum number of samples chosen from this group exclude the seeds</param>
        public void RemoveDuplicationGroupWithEvaluation(List<Webpage> curGroup, int percentOfSeed, int minNumOfSeed, int percentOfSample, int minNumOfSample)
        {
            // Load pages in the group
            List<Webtag> wtagLst = new List<Webtag>();
            foreach (Webpage i in curGroup)
            {
                wtagLst.Add(i.WebtagTree);
            }

            Webtag maxSimWtag; // The webtag which has the max similarity with the chosen seed
            Webtag chosenSeed = ChooseSeed(new List<Webtag>(wtagLst), out maxSimWtag, percentOfSeed, minNumOfSeed
                , percentOfSample, minNumOfSample); // Choose the seed

            Console.WriteLine("Remove dupplication!");
            wtagLst.Remove(chosenSeed);
            wtagLst.Remove(maxSimWtag);

            List<Webtag> semiWtagList = new List<Webtag>();
            for (int i = 0; i < wtagLst.Count; i++)
            {
                MatchTwoWebtags(chosenSeed, wtagLst[i]);
                if (MeasureSimilarity(wtagLst[i]) == 0)
                {
                    semiWtagList.Add(wtagLst[i]);
                }
                else
                {
                    Console.WriteLine(MeasureSimilarity(wtagLst[i]));
                    RemoveDuplicatedPart(wtagLst[i], curGroup[GroupIndex(wtagLst[i].WebpageID, curGroup)].TextTagList);
                }
            }

            if (semiWtagList.Count == 1)
            {
                MatchTwoWebtags(maxSimWtag, semiWtagList[0]);
                RemoveDuplicatedPart(semiWtagList[0], curGroup[GroupIndex(semiWtagList[0].WebpageID, curGroup)].TextTagList);
            }

            MatchTwoWebtags(chosenSeed, maxSimWtag);
            RemoveDuplicatedPart(chosenSeed, curGroup[GroupIndex(chosenSeed.WebpageID, curGroup)].TextTagList);
            RemoveDuplicatedPart(maxSimWtag, curGroup[GroupIndex(maxSimWtag.WebpageID, curGroup)].TextTagList);

            if (semiWtagList.Count >= 2)
            {
                chosenSeed = ChooseSeed(new List<Webtag>(semiWtagList), out maxSimWtag, percentOfSeed, minNumOfSeed, percentOfSample, minNumOfSample);
                Console.WriteLine("Remove dupplication the 2nd time!");
                semiWtagList.Remove(chosenSeed);
                semiWtagList.Remove(maxSimWtag);

                for (int i = 0; i < semiWtagList.Count; i++)
                {
                    MatchTwoWebtags(chosenSeed, semiWtagList[i]);
                    RemoveDuplicatedPart(semiWtagList[i], curGroup[GroupIndex(semiWtagList[i].WebpageID, curGroup)].TextTagList);
                }

                MatchTwoWebtags(chosenSeed, maxSimWtag);
                RemoveDuplicatedPart(chosenSeed, curGroup[GroupIndex(chosenSeed.WebpageID, curGroup)].TextTagList);
                RemoveDuplicatedPart(maxSimWtag, curGroup[GroupIndex(maxSimWtag.WebpageID, curGroup)].TextTagList);
            }
        }

        /// <summary>
        /// Measure the height of the duplicated subtree.
        /// </summary>
        /// <param name="wtag">The duplicated subtree.</param>
        /// <returns>Height of this duplicated subtree.</returns>
        public int MeasureHeight(Webtag wtag)
        {
            if (wtag.IsDup == 1)
            {
                if (wtag.Children.Count == 0)
                {
                    return 1;
                }
                else
                {
                    int height = 0;
                    for (int i = 0; i < wtag.Children.Count; i++)
                    {
                        int temp = MeasureHeight(wtag.Children[i]);
                        if (temp > height)
                        {
                            height = temp;
                        }
                    }

                    return height + 1;
                }
            }
            else
            {
                return 0;
            }
        }

        /// <summary>
        /// Get the index of the webpage in list of webpages by webpage ID
        /// </summary>
        /// <param name="webpageID">The ID of the webpage that needs to get index</param>
        /// <param name="curGroup">The list of webpages</param>
        /// <returns>Index of the webpage in this group</returns>
        private int GroupIndex(int webpageID, List<Webpage> curGroup)
        {
            for (int i = 0; i < curGroup.Count; i++)
            {
                if (curGroup[i].ID == webpageID)
                    return i;
            }
            return -1;
        }

        /// <summary>
        /// Randomly choose a seed from a group
        /// </summary>
        /// <param name="wtagLst">List of tag trees of pages in a group</param>
        /// <param name="maxSimPos">The position of the sample which has the highes similarity with the tested seed</param>
        /// <returns>Index of the chosen seed in a group</returns>
        public Webtag ChooseSeed(List<Webtag> wtagLst, out Webtag maxSimWtag
            , int percentOfSeed, int minNumOfSeed
            , int percentOfSample, int minNumOfSample)
        {
            if ((percentOfSample > 100) || (percentOfSample <= 0)
                || (percentOfSeed > 100) || (percentOfSeed <= 0)
                || (minNumOfSample < 0) || (minNumOfSample > wtagLst.Count)
                || (minNumOfSeed < 0) || (minNumOfSeed > wtagLst.Count))
            {
                maxSimWtag = null;
                return null;
            }

            int n = (int)((percentOfSeed * 1.0 / 100.0) * wtagLst.Count); // the number of seed
            int m = (int)((percentOfSample * 1.0 / 100.0) * wtagLst.Count); // the number of sample
            // Choose the number of seed and sample
            n = (n < minNumOfSeed) ? minNumOfSeed : n;
            m = (m < minNumOfSample) ? minNumOfSample : m;

            List<Webtag> seedList = ArrayLib.GetRandomElements<Webtag>(n, wtagLst);

            int maxSim = 0;
            int minSim = int.MaxValue;
            double meanSim = -1.0;
            double SDSim = -1.0;

            Webtag chosenSeed = new Webtag();
            List<double> meanLst = new List<double>();
            List<double> SDLst = new List<double>();
            List<Webtag> maxSimLst = new List<Webtag>();
            maxSimWtag = new Webtag();

            foreach (Webtag seed in seedList)
            {
                maxSimLst.Add(EvaluateSeed(wtagLst, seed, m, out minSim, out maxSim, out meanSim, out SDSim));
                meanLst.Add(meanSim);
                SDLst.Add(SDSim);
            }

            int tempIndex = StatisticLib.EvaluateMeanAndSD(meanLst, SDLst);
            chosenSeed = seedList[tempIndex];
            maxSimWtag = maxSimLst[tempIndex];

            //Console.WriteLine("minSim= " + minSim + " maxSim= " + maxSim + " meanSim= " + meanSim + " SDSim= " + SDSim);          

            return chosenSeed;
        }

        /// <summary>
        /// Evaluate the chosen seed in a group through the max, min, mean, standard deviation similarity with other pages in the group.
        /// </summary>
        /// <param name="wtagLst">List of tag trees of pages in a group</param>
        /// <param name="seedIndex">Index of the seed in this group.</param>
        /// <param name="numOfSamples">The number of sample used to compared with this seed.</param>
        /// <param name="minSim">Minimum similarity of the samples in the group with chosen seed.</param>
        /// <param name="maxSim">Maximum similarity of the samples in the group with chosen seed.</param>
        /// <param name="meanSim">Mean similarity of the samples in the group with chosen seed.</param>
        /// <param name="SDSim">Standard deviation of the samples in the group with chosen seed.</param>
        /// <returns>The index of a sample which have the maximum similarity with this seed.</returns>
        private Webtag EvaluateSeed(List<Webtag> wtagLst, Webtag seed, int numOfSamples
            , out int minSim, out int maxSim, out double meanSim, out double SDSim)
        {
            // nên dùng arraylib để lấy ra randomly list các webtag không dính seed trong đó
            List<Webtag> sampleList = new List<Webtag>(wtagLst);
            sampleList.Remove(seed);
            sampleList = ArrayLib.GetRandomElements<Webtag>(numOfSamples, sampleList);
            List<int> sim = new List<int>();
            foreach (Webtag curSampleWebtag in sampleList)
            {
                MatchTwoWebtags(seed, curSampleWebtag);
                int temp = MeasureSimilarity(seed);
                sim.Add(temp);
                Console.WriteLine("sim in evaluate seed = " + temp);
            }

            SDSim = StatisticLib.MeasureStandardDeviation(sim);
            minSim = sim.Min();
            maxSim = sim.Max();
            int maxPos = sim.IndexOf(maxSim);
            meanSim = StatisticLib.MeasureMean(sim);

            return sampleList[maxPos];
        }

        /// <summary>
        /// Remove duplicated subtree of a page.
        /// </summary>
        /// <param name="wtag">The MatchTwoWebtags-ed tag tree of this page</param>
        /// <param name="listWebpageTags">List of all webtags of this webpage</param>
        public void RemoveDuplicatedPart(Webtag wtag, List<Webtag> listWebpageTags)
        {
            if (wtag.IsDup == -1)
            {
                return;
            }
            else
            {
                if (wtag.IsDup == 1)
                {
                    RemoveWebTagXPath(wtag, listWebpageTags);
                }
                else
                {
                    for (int i = 0; i < wtag.Children.Count; i++)
                    {
                        RemoveDuplicatedPart(wtag.Children[i], listWebpageTags);
                    }
                }
            }
        }

        /// <summary>
        /// Remove the webtag from the webtag list
        /// </summary>
        /// <param name="curWebtag">The webtag that contains the information of the deleted webtag.</param>
        /// <param name="listWebpageTags">The list of webtags of this page.</param>
        private void RemoveWebTagXPath(Webtag curWebtag, List<Webtag> listWebpageTags)
        {
            // check whether this tag is a leaf or not
            if (curWebtag.Children.Count == 0)
            {
                for (int i = 0; i < listWebpageTags.Count; i++)
                {
                    if (listWebpageTags[i].XPath == curWebtag.XPath)
                    {
                        listWebpageTags.Remove(listWebpageTags[i]);
                        break;
                    }
                }
            }
            else    // this tag is not a leaf
            {
                foreach (Webtag i in curWebtag.Children)
                {
                    RemoveWebTagXPath(i, listWebpageTags);
                }
            }
        } 
        #endregion
    }
}
